package gui.batches;

import gui.commands.CommandHistory;
import gui.commands.RemoveItemCommand;
import gui.common.Controller;
import gui.common.IView;
import gui.item.ItemData;
import gui.product.ProductData;

import java.util.*;

import model.entity.Item;
import model.entity.Product;
import model.events.EntityEvent;
import model.management.ItemManager;

import common.ValueResult;

/**
 * Controller class for the remove item batch view.
 */
public class RemoveItemBatchController extends Controller implements IRemoveItemBatchController
{
  private List<ProductData>                products = new ArrayList<ProductData>();
  private Map<ProductData, List<ItemData>> items    = new HashMap<ProductData, List<ItemData>>();

  private Timer                            timer;
  private TimerTask                        task;

  private CommandHistory                   history;

  /**
   * Constructor.
   * 
   * @param view
   *          Reference to the remove item batch view.
   */
  public RemoveItemBatchController(IView view)
  {
    super(view);

    timer = new Timer();
    history = new CommandHistory();

    construct();
  }

  /**
   * Returns a reference to the view for this controller.
   */
  @Override
  protected IRemoveItemBatchView getView()
  {
    return (IRemoveItemBatchView) super.getView();
  }

  /**
   * Loads data into the controller's view.
   * 
   * {@pre None}
   * 
   * {@post The controller has loaded data into its view}
   */
  @Override
  protected void loadValues()
  {
    getView().setBarcode("");
    getView().setUseScanner(true);
    getView().giveBarcodeFocus();
    enableComponents();
  }

  /**
   * Sets the enable/disable state of all components in the controller's view. A component should
   * be enabled only if the user is currently allowed to interact with that component.
   * 
   * {@pre None}
   * 
   * {@post The enable/disable state of all components in the controller's view have been set
   * appropriately.}
   */
  @Override
  protected void enableComponents()
  {
    String barcode = getView().getBarcode();
    boolean barcodeValid = barcode != null && !barcode.isEmpty();
    boolean scanner = getView().getUseScanner();
    boolean valid = barcodeValid && !scanner;
    getView().enableItemAction(valid);

    getView().enableUndo(history.canUndo());
    getView().enableRedo(history.canRedo());
  }

  /**
   * This method is called when the "Item Barcode" field is changed in the remove item batch view
   * by the user.
   */
  @Override
  public void barcodeChanged()
  {
    if (!getView().getUseScanner())
      enableComponents();
    else
    {
      if (task != null)
        task.cancel();

      timer.schedule(getScannerResponseTask(), 200);
    }
  }

  /**
   * This method is called when the "Use Barcode Scanner" setting is changed in the remove item
   * batch view by the user.
   */
  @Override
  public void useScannerChanged()
  {
    enableComponents();
  }

  /**
   * This method is called when the selected product changes in the remove item batch view.
   */
  @Override
  public void selectedProductChanged()
  {
    ProductData pdata = getView().getSelectedProduct();
    updateItemList(pdata);
  }

  /**
   * This method is called when the user clicks the "Remove Item" button in the remove item batch
   * view.
   */
  @Override
  public void removeItem()
  {
    String barcode = getView().getBarcode();
    Item item = ItemManager.getItem(barcode);

    if (item == null)
    {
      getView().displayErrorMessage("ERROR: The requested item was not found!");
      return;
    }

    ValueResult<EntityEvent> result = ItemManager.removeItem(item);
    if (result.getStatus() == true)
    {
      history.add(new RemoveItemCommand(item));
      addItemToList(item);
      updateDisplay();
    }
    else
      getView().displayErrorMessage(
          "ERROR: The requested item could not be removed!\n" + result.getMessage());

    loadValues();
  }

  private void addItemToList(Item item)
  {
    Product product = item.getProduct();
    ProductData pdata = product.generateProductDataObject(item.getContainer());

    if (!products.contains(pdata))
    {
      products.add(pdata);
      items.put(pdata, new ArrayList<ItemData>());
    }

    List<ItemData> removedItems = items.get(pdata);
    removedItems.add(item.generateItemDataObject());
  }

  private void updateDisplay()
  {
    ProductData product = updateProductList();
    updateItemList(product);
  }

  private ProductData updateProductList()
  {
    ProductData selectedData = getView().getSelectedProduct();

    ProductData[] pdata = products.toArray(new ProductData[0]);

    getView().setProducts(pdata);
    getView().selectProduct(selectedData);

    return selectedData;
  }

  private void updateItemList(ProductData product)
  {
    if (product != null)
    {
      ItemData selectedData = getView().getSelectedItem();
      List<ItemData> removedItems = items.get(product);

      ItemData[] idata = removedItems.toArray(new ItemData[0]);

      getView().setItems(idata);
      getView().selectItem(selectedData);
    }
    else
      getView().setItems(new ItemData[0]);
  }

  /**
   * This method is called when the user clicks the "Redo" button in the remove item batch view.
   */
  @Override
  public void redo()
  {
    history.redo();
  }

  /**
   * This method is called when the user clicks the "Undo" button in the remove item batch view.
   */
  @Override
  public void undo()
  {
    history.undo();
  }

  /**
   * This method is called when the user clicks the "Done" button in the remove item batch view.
   */
  @Override
  public void done()
  {
    getView().close();
  }

  private TimerTask getScannerResponseTask()
  {
    return task = new TimerTask()
    {
      @Override
      public void run()
      {
        removeItem();
      }
    };
  }
}
